<?php

namespace Albireo\OAuth;

use Nette\Object;
use \Nette\Web\Uri;

use \OAuth;
use \OAuthException;

use Celebrio\ShadowReader;

/**
 * OAuthRequestHandler. Serves as simple library for handling client side of
 * OAuth protocol. Provides the methods for all kinds of requests. The client
 * information (client_id and secret) should be stored in the shadow.ini file.
 *
 * @author pavel
 */

class OAuthClient extends Object {

    const FB = "fb";
    const GOOGLE = "google";

    const REQUEST = "request";
    const AUTHORIZE = "authorize";
    const ACCESS = "access";

    /** @var \OAuth */
    private $oAuth;

    /** @var string */
    private $kind;

    /** @var array */
    private $credentials;

    /** @var array */
    private $urls;
    
    public function __construct($kind) {

        $shadowReader = new ShadowReader();
        $credentials = $shadowReader->getOauth();

        if (isset($credentials[$kind])) {
            $this->kind = $kind;
            $this->credentials = $credentials[$kind];
            $this->oAuth = new OAuth($credentials[$kind]["key"], $credentials[$kind]["secret"]);
            $this->setUpUrls();
        } else {
            throw new \InvalidArgumentException("Invalid kind for OAuth client given: $kind. We support only: " . array_keys($credentials));
        }

    }

    public function setOAuthUrl($type, $requestUrl) {
        $this->urls[$this->kind][$type] = $requestUrl;
    }

    public function getCredentials() {
        return $this->credentials;
    }
    
    /**
     * Gets the OAuth Request Token from google server.
     * 
     * @param String $callbackUrl The URL we want to get redirected after OAuthorization
     * @param mixed $scope String or array(String) representing the scope we want
     *        to access. It's google specific parameter.
     * @return array(String => String/bool) The array of succesfuly recieved request
     *          token from google with its additional data. It looks just like this:
     *              array(
     *                  "oauth_token" => "4/K7mLjree3Kf3nxmg3VDbJ_8FVCJ7"
     *                  "oauth_token_secret" => "P3IXvLecZtwoLkqPejaUOmu9"
     *                  "oauth_callback_confirmed" => "true"
     *              );
     * 
     * @throws OAuthException when something screws up
     */
    public function getRequestToken($callbackUrl, $scope = array(), $scopeGlue = ",") {
        
        try {
            return $this->oAuth->getRequestToken($this->getRequestTokenUri($callbackUrl, $scope, $scopeGlue), $callbackUrl);
        } catch (OAuthException $e) {
            throw $e;
        }
        
    }

    public function getRequestTokenUri($callbackUrl, $scope = array(), $scopeGlue = ",", $state = array()) {
        if ($callbackUrl === null) {
            throw new \Exception("Null callback URL");
        }
        
        $uri = new Uri($this->urls[$this->kind][self::REQUEST]);
        $uri->appendQuery(array("scope" => is_array($scope) ? implode($scopeGlue, $scope) : $scope));
        $uri->appendQuery(array("state" => is_array($state) ? implode(",", $state) : $state));
        if ($this->kind === self::FB) {
            $uri->appendQuery(array("client_id" => $this->credentials["key"]));
            $uri->appendQuery(array("response_type" => "code"));
            $uri->appendQuery(array("redirect_uri" => $callbackUrl));
        }
        
        return $uri;
    }
    
    /**
     * Returns the URL user should be redirected to grant authorization.
     * 
     * @param array $requestTokenInfo 
     */
    public function authorizationUrl(array $requestTokenInfo) {
        $urlBuilder = new Uri($this->urls[$this->kind][self::AUTHORIZE]);
        $urlBuilder->addParameter("oauth_token", $requestTokenInfo["oauth_token"]);
        return $urlBuilder->build();
    }
    
    public function getAccessToken($authToken, $tokenSecret) {
        $this->oAuth->setToken($authToken, $tokenSecret);
        try {
            $result = $this->oAuth->getAccessToken(self::urlAccessToken);
            return $result;
        } catch (OAuthException $e) {
            //echo $e->lastResponse;
            throw $e;
        }
    }
    
    public function fetch($accessToken, $accessTokenSecret, $url) {
        $this->oAuth->setToken($accessToken, $accessTokenSecret);
        try {
            $result = $this->oAuth->fetch($url, null, OAUTH_HTTP_METHOD_GET, array('GData-Version'=>'3.0'));
            \Nette\Debug::dump($this->oAuth->getLastResponseInfo());
            return $this->oAuth->getLastResponse();
        } catch (OAuthException $e) {
            echo $e->lastResponse;
            //throw $e;
        }
    }


    // -------------------------------------------------------------------


    private function setUpUrls() {
        $this->urls = array(
            self::FB => array(
                self::REQUEST => "https://www.facebook.com/dialog/oauth",
//                self::AUTHORIZE => "https://www.facebook.com/oauth/authorize",
//                self::ACCESS => "",
            ),
            self::GOOGLE => array(
                self::REQUEST => "https://www.google.com/accounts/OAuthGetRequestToken",
                self::AUTHORIZE => "https://www.google.com/accounts/OAuthAuthorizeToken",
                self::ACCESS => "https://www.google.com/accounts/OAuthGetAccessToken",
            ),
        );
    }
}